package d1_midterm_examination.demo06;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

/**
 * 1、设计一个Node泛型类，用于代表链表的结点。每个结点包含（数据data，和下一个结点的地址值next） 3
 * 2、开发一个类叫MyLinkedList，提供一个add方法可以让用户添加链表的结点，直到用户输入exit，则返回链表（返回链表实际上是返回链表的头结点） 5
 * 3、提供一个reverse方法，接收头指针 head 和两个整数 left 和 right ，其中 left <= right，按上面的要求进行反转。反转后，返回新的链表 9
 * 4、提供一个forEach方法，接收新链表，并对其进行遍历输出。 5
 */
public class MyLinkedList02<E> {
    //3、直接定义在最外面,不让它老是报, 黄
    private Node<E> head = null;

    //5、定义一个变量记住链表元素的个数
    private int size;

    //1、首先设计一个静态内部类,代表节点
    public static class Node<E> {
        //1.定义一个变量用于记录节点的数据
        String data;
        //2.定义一个变量用于记录下一个节点的地址
        Node<E> next;
        //3.提供创建节点得构造器

        public Node(String data, Node<E> next) {
            this.data = data;
            this.next = next;
        }
    }

    //4、提供一个方法reverse, 接头指针head和两个整数left和right, 其中left <= right 按照上面要求进行反转。反转后, 返回新的链表
    public Node<E> reverse(Node<E> head, int left, int right) {
        //1.首先做极端判断,这些情况,不进行反转
        if (head == null || head.next == null || left > right || left < 1 || left > size || right > size) {
            //不进行反转,直接结束方法
            return head;
        }
        //2.开始反转
        //3.定义一个变量记住要反转的起始点
        Node<E> start = null;
        //4.不要动头节点 -->再次定义一个临时节点,用于操作
        Node<E> first = head;
        //5.思想就是,将要反转的数据,存到集合中, 然后,倒着遍历集合,将数据存到要反转的位置
        List<String> datas = new ArrayList<>();
        //6.定义一个变量,记住变量到什么位置了,用于校验是否到了开始反转的位置
        int index = 1;//直接从1开始遍历
        //1 2 3 4 5 6
        //
        //7.开始循环
        while (first.next != null) {

            //8.判断是否已经到了开始开始反转的节点
            if (index == left) {
                //9.记住要反转的节点
                start = first;
                /*//12.把当前数据存到集合中去
                datas.add(start.data);*/
            }
            //13.就开始存数据到集合中去
            if (index >= left && index <= right) {
                datas.add(first.data);
            }

            if (index == right) {
                //11.进行循环
                break;
            }
            //10.让它持续往下找
            first = first.next;
            ++index;
        }

        //13.开始反转,直接遍历集合
        for (int i = datas.size() - 1; i >= 0; i--) {
            String data = datas.get(i);
            start.data = data;
            start = start.next;
        }
        return head;
    }

    //3、提供一个forEach方法, 接受新链表, 并对其进行遍历输出
    public void forEach(Node<E> head) {
        //1.做极端判断
        if (head == null) {
            //2.直接进行输出head, 不进行下一步遍历
            System.out.println(head);
            //3.干掉方法
            return;
        }

        //4.首先将头结点的数据拿出来
        System.out.print(head.data + " ");
        //5.开始循环,遍历集合的其他节点
        while (head.next != null) {
            head = head.next;
            //6.将数据记输出
            System.out.print(head.data + " ");
        }
    }

    //2、提供一个`add方法可以让用户添加,链表的节点, 直到用户输入exit, 则返回链表(返回链表的头结点)
    public Node<E> add() {
        //1.创建一个节点代表头节点

        //3.创建一个扫描器
        Scanner sc = new Scanner(System.in);
        //10.让用户反复添加数据
        while (true) {
            //4.提示用户输入要存储的数据
            System.out.println("请输出您要存储的数据");
            String data = sc.next();
            //1.退出条件
            if ("exit".equals(data)) {
                break;
            }
            //2.做判断,看头结点是不是null,如果是就代表是第一次添加元素
            if (head == null) {
                //5.是第一次创建节点-->创建一个节点赋值给head
                head = new Node<>(data, null);
                ++size;
            } else {
                //6.不是第一次创建节点,这时就需要循环找到最后一个节点的位置-->但是不可以动头节点
                //定义一个临时节点,记录头结点
                Node<E> temp = head;
                //7.开始循环
                while (temp.next != null) {
                    temp = temp.next;
                }

                //8.到这里就证明是最后一个节点了 -->创建一个节点,赋值给temp
                temp.next = new Node<>(data, null);
                ++size;
            }
        }


        //9.最后将头节点进行返回
        return head;
    }
}
